Functions¶
TTP contains a set of TTP match variables functions that can be applied to match results to transform them in a desired way or validate and filter match results.
Action functions act upon match result to transform into desired state.
Name | Description |
---|---|
chain | add functions from chain variable |
record | Save match result to variable with given name, which can be referenced by actions |
let | Assigns provided value to match variable |
truncate | truncate match results |
joinmatches | join matches using provided character |
resub | replace old patter with new pattern in match using re substitute method |
join | join match using provided character |
append | append provided string to the end of match result |
prepend | prepend provided string at the beginning of match result |
print match result to terminal | |
unrange | unrange match result using given parameters |
set | set result to specific value based if certain string was matched |
replaceall | run replace against match for all given values |
resuball | run re substitute against match for all given values |
lookup | find match value in lookup table and return result |
rlookup | find rlookup table key in match result and return associated values |
gpvlookup | Glob Patterns Values lookup uses glob patterns testing against match result |
geoip_lookup | Uses GeoIP2 database to lookup ASN, Country or City information |
item | returns item at given index on match result |
macro | runs match result against macro function |
to_list | creates empty list nd appends match result to it |
to_int | transforms result to integer |
to_str | transforms result to python string |
to_ip | transforms result to python ipaddress module IPvXAddress or IPvXInterface object |
to_net | transforms result to python ipaddress module IPvXNetwork object |
to_cidr | transforms netmask to cidr (prefix length) notation |
ip_info | produces a dictionary with information about give ip address or subnet |
dns | performs DNS forward lookup |
rdns | performs DNS reverse lookup |
sformat | string format using python string format method |
uptimeparse | function to parse uptime string |
mac_eui | transforms mac string into EUI format |
count | function to count matches |
void | returns False on results validation, allowing to skip them |
to_float | converts match variable value to float integer |
to_unicode | if script run by python2, converts string to unicode |
Condition functions can perform various checks with match results and returns either True or False depending on check results.
Name | Description |
---|---|
equal | check if match is equal to provided value |
notequal | check if match is not equal to provided value |
startswith_re | checks if match starts with certain string using regular expression |
endswith_re | checks if match ends with certain string using regular expression |
contains_re | checks if match contains certain string using regular expression |
contains | checks if match contains certain string patterns |
notstartswith_re | checks if match not starts with certain string using regular expression |
notendswith_re | checks if match not ends with certain string using regular expression |
exclude_re | checks if match not contains certain string using regular expression |
exclude | checks if match not contains certain string |
isdigit | checks if match is digit string e.g. ‘42’ |
notdigit | checks if match is not digit string |
greaterthan | checks if match is greater than given value |
lessthan | checks if match is less than given value |
is_ip | tries to convert match result to ipaddress object and returns True if so, False otherwise |
cidr_match | transforms result to ipaddress object and checks if it overlaps with given prefix |
Python built-ins¶
Apart from functions provided by ttp, python objects built-in functions can be used as well. For instance string upper method can be used to convert match into upper case, or list index method to return index of certain value.
Example
Data:
interface Tunnel2422
description cpe-1
!
interface GigabitEthernet1/1
description core-1
Template:
<group name="interfaces">
interface {{ interface | upper }}
description {{ description | split('-') }}
</group>
Result:
{
"interfaces": [
{
"description": ["cpe", "1"],
"interface": "TUNNEL2422"
},
{
"description": ["core", "1"],
"interface": "GIGABITETHERNET1/1"
}
]
}
chain¶
{{ name | chain(variable_name) }}
- variable_name (mandatory) - string containing variable name
Sometime when many functions needs to be run against match result the template can become difficult to read, in addition if same set of functions needs to be run against several matches and changes needs to be done to the set of functions it can become difficult to maintain such a template.
To solve above problem chain function can be used. Value supplied to that function must reference a valid variable name, that variable should contain string of functions names that should be used for match result, alternatively variable can reference a list of items, each item is a string representing function to run.
Example-1
chain referencing variable that contains string of functions separated by pipe symbol.
Data:
interface GigabitEthernet3/3
switchport trunk allowed vlan add 138,166-173
switchport trunk allowed vlan add 400,401,410
Template:
<vars>
vlans = "unrange(rangechar='-', joinchar=',') | split(',') | join(':') | joinmatches(':')"
</vars>
<group name="interfaces">
interface {{ interface }}
switchport trunk allowed vlan add {{ trunk_vlans | chain('vlans') }}
</group>
Result:
{
"interfaces": {
"interface": "GigabitEthernet3/3",
"trunk_vlans": "138:166:167:168:169:170:171:172:173:400:401:410"
}
}
Example-2
chain referencing variable that contains list of strings, each string is a function.
Data:
interface GigabitEthernet3/3
switchport trunk allowed vlan add 138,166-173
switchport trunk allowed vlan add 400,401,410
Template:
<vars>
vlans = [
"unrange(rangechar='-', joinchar=',')",
"split(',')",
"join(':')",
"joinmatches(':')"
]
</vars>
<group name="interfaces">
interface {{ interface }}
switchport trunk allowed vlan add {{ trunk_vlans | chain('vlans') }}
</group>
Result:
{
"interfaces": {
"interface": "GigabitEthernet3/3",
"trunk_vlans": "138:166:167:168:169:170:171:172:173:400:401:410"
}
}
record¶
{{ name | record(var_name) }}
- var_name (mandatory) - template variable name that should be used to record match result
Record match results in template variable with given name. That recorded variable can be referenced within other functions such as set or retrieved from _ttp_ dictionary within macro.
- Variables are recorded in two scopes:
- Per-Input scope - all groups that parse this particular input will have access to recorded variable; variable stored in
_ttp_["parser_object"].vars
dictionary - Global scope - variable available from any group at any template; variable stored in
_ttp_["global_vars"]
dictionary
- Per-Input scope - all groups that parse this particular input will have access to recorded variable; variable stored in
Warning
record results override one another, meaning if several match variable record result in same template variable, match variable that was matched later will override previous match result.
Example
Template:
<input load="text" name="in1">
myswitch1#show run int
interface Vlan778
ip vrf forwarding VRF_NAME_1
ip address 2002:fd37::91/124
!
</input>
<input load="text" name="in2">
myswitch2#show run int
interface Vlan779
description some description input2
!
interface Vlan780
switchport port-security mac 4
!
</input>
<group name="interfaces" input="in1">
interface {{ interface }}
ip address {{ ip }}/{{ mask }}
ip vrf forwarding {{ vrf | record("VRF") }}
switchport port-security mac {{ sec_mac }}
</group>
<group name="interfaces" input="in2">
interface {{ interface }}
description {{ description | ORPHRASE | record("my_description") }}
switchport port-security mac {{ sec_mac }}
{{ my_vrf | set("VRF") }}
{{ my_descript | set("my_description") }}
</group>
Result:
[
{
"interfaces": {
"interface": "Vlan778",
"ip": "2002:fd37::91",
"mask": "124",
"vrf": "VRF_NAME_1"
}
},
{
"interfaces": [
{
"description": "some description input2",
"interface": "Vlan779",
"my_descript": "some description input2",
"my_vrf": "VRF_NAME_1"
},
{
"interface": "Vlan780",
"my_descript": "some description input2",
"my_vrf": "VRF_NAME_1",
"sec_mac": "4"
}
]
}
]
In above example {{ my_vrf | set("VRF") }}
uses “VRF” variable from Global scope, while {{ my_descript | set("my_description") }}
retrieves “my_description” variable value from per-input scope.
let¶
{{ variable | let(var_name, value) }}
or {{ variable | let(value) }}
- value (mandatory) - a string containing value to be assigned to variable
Statically assigns provided value to variable with name var_name, if single argument provided, that argument considered to be a value and will be assigned to match variable replacing match result.
Example
Template:
<input load="text">
interface Loopback0
description Management
ip address 192.168.0.113/24
!
</input>
<group name="interfaces">
interface {{ interface }}
description {{ description | let("description_undefined") }}
ip address {{ ip | contains("24") | let("netmask", "255.255.255.0") }}
</group>
Result:
[
{
"interfaces": {
"description": "description_undefined",
"interface": "Loopback0",
"ip": "192.168.0.113/24",
"netmask": "255.255.255.0"
}
}
]
truncate¶
{{ name | truncate(count) }}
- count (mandatory) - integer to count the number of words to remove
Splits match result using ” “(space) char and joins it back up to truncate value. This function can be useful to shorten long match results.
Example
If match is “foo bar foo-bar” and truncate(2) will produce “foo bar”.
joinmatches¶
{{ name | joinmatches(char) }}
- char (optional) - character to use to join matches, default is new line ‘\n’
Join results from different matches into a single result string using provider character or string.
In case if data items passed to joinmatches
are lists, joinmatches
will combine them in one single list, if any of the items is a string and at list one of the items is a list, all items will be combined in a list as well. For instance, to convert match results to a list to_list function can be used.
Warning
if template line contains match variable with joinmatches
, all variables in that line should have joinmatches
defined, otherwise match results will not be joined. Moreover, joinmatches
should use same join character for predictable results. Only ignore
indicator supported for line with joinmatches
variables.
Example-1
Data:
interface GigabitEthernet3/3
switchport trunk allowed vlan add 138,166,173
switchport trunk allowed vlan add 400,401,410
Template:
interface {{ interface }}
switchport trunk allowed vlan add {{ trunk_vlans | joinmatches(',') }}
Result:
{
"interface": "GigabitEthernet3/3"
"trunkVlans": "138,166,173,400,401,410"
}
Example-2
Using to_list
function to join results in a list.
Data:
interface GigabitEthernet3/3
switchport trunk allowed vlan add 138,166,173
switchport trunk allowed vlan add 400,401,410
Template:
interface {{ interface }}
switchport trunk allowed vlan add {{ trunk_vlans | to_list | joinmatches }}
Result:
{
"interface": "GigabitEthernet3/3"
"trunkVlans": ["138,166,173", "400,401,410"]
}
resub¶
{{ name | resub(old, new, count) }}
- old (mandatory) - pattern to be replaced, can reference template variable name
- new (mandatory) - pattern to be replaced with
- count(optional) - digit, default is 1, indicates count of replacements to do
Performs re.sub(old, new, match, count) on match result and returns produced value
Example
Data:
interface GigabitEthernet3/3
Template is:
interface {{ interface | resub(old = '^GigabitEthernet'), new = 'Ge'}}
Result:
{
"interface": "Ge3/3"
}
join¶
{{ name | match(char) }}
- char (mandatory) - character to use to join match
Run joins against match result using provided character and return string
Example-1:
Match is a string here and running join against it will insert ‘.’ in between each character
Data:
description someimportantdescription
Template is:
description {{ description | join('.') }}
Result:
{
"description": "s.o.m.e.i.m.p.o.r.t.a.n.t.d.e.s.c.r.i.p.t.i.o.n"
}
Example-2:
After running split function match result transformed into list object, running join against list will produce string with values separated by “:” character
Data:
interface GigabitEthernet3/3
switchport trunk allowed vlan add 138,166,173,400,401,410
Template:
interface {{ interface }}
switchport trunk allowed vlan add {{ trunk_vlans | split(',') | join(':') }}
Result:
{
"interface": "GigabitEthernet3/3"
"trunkVlans": "138:166:173:400:401:410"
}
append¶
{{ name | append(string) }}
- string (mandatory) - string to append
Appends string to match result and returns produced value
Example
Data:
interface Ge3/3
Template is:
interface {{ interface | append(' - non production') }}
Result:
{
"interface": "Ge3/3 - non production"
}
prepend¶
{{ name | prepend(string) }}
- string (mandatory) - string to prepend
Prepends string to match result and returns produced value
print¶
{{ name | print }}
Will print match result to terminal as is at the given position, can be used for debugging purposes
Example
Data:
interface GigabitEthernet3/3
switchport trunk allowed vlan add 138,166,173
Template:
interface {{ interface }}
switchport trunk allowed vlan add {{ trunk_vlans | split(',') | print | join(':') print }}
Results printed to terminal:
['138', '166', '173'] <--First print statement
138:166:173 <--Second print statement
unrange¶
{{ name | unrange('rangechar', 'joinchar') }}
- rangechar (mandatory) - character to indicate range
- joinchar (mandatory) - character used to join range items
If match result has integer range in it, this function can be used to extend that range to specific values, For instance if range is 100-105, after passing that result through this function result ‘101,102,103,104,105’ will be produced. That is useful to extend trunk vlan ranges configured on interface.
Example
Data:
interface GigabitEthernet3/3
switchport trunk allowed vlan add 138,166,170-173
Template:
interface {{ interface }}
switchport trunk allowed vlan add {{ trunk_vlans | unrange(rangechar='-', joinchar=',') }}
Result:
{
"interface": "GigabitEthernet3/3"
"trunkVlans": "138,166,170,171,172,173"
}
set¶
{{ name | set('var_set_value') }}
- var_set_value (mandatory) - string to set as a value for variable, can be a name of template variable.
Not all configuration statements have variables or values associated with them, but can serve as an indicator if particular feature disabled or enabled, to match such a cases set function can be used. This function allows to assign “var_set_value” to match variable, “var_set_value” considered to be a reference to template variable name, if no template variable with “var_set_value” found, “var_set_value” itself will be assigned to match variable.
It is also possible to use set function to introduce arbitrary key-value pairs in match result if set function used without any text in front of it.
Example-1
Conditional set function - set only will be invoked in case if preceding line matched. In below example ” switchport trunk encapsulation dot1q” line will be searched for, if found, “encap” variable will have “dot1q” value set.
Data:
interface GigabitEthernet3/4
switchport mode access
switchport trunk encapsulation dot1q
switchport mode trunk
switchport nonegotiate
shutdown
!
interface GigabitEthernet3/7
switchport mode access
switchport mode trunk
switchport nonegotiate
!
Template:
<vars>
mys_set_var = "my_set_value"
</vars>
<group name="interfacesset">
interface {{ interface }}
switchport mode access {{ mode_access | set("True") }}
switchport trunk encapsulation dot1q {{ encap | set("dot1q") }}
switchport mode trunk {{ mode | set("Trunk") }} {{ vlans | set("all_vlans") }}
shutdown {{ disabled | set("True") }} {{ test_var | set("mys_set_var") }}
!{{ _end_ }}
</group>
Result:
{
"interfacesset": [
{
"disabled": "True",
"encap": "dot1q",
"interface": "GigabitEthernet3/4",
"mode": "Trunk",
"mode_access": "True",
"test_var": "my_set_value",
"vlans": "all_vlans"
},
{
"interface": "GigabitEthernet3/7",
"mode": "Trunk",
"mode_access": "True",
"vlans": "all_vlans"
}
]
}
Note
Multiple set statements are supported within the line, however, no other variables can be specified except with set, as match performed based on the string preceding variables with set function, for instance below will not work: switchport mode {{ mode }} {{ switchport_mode | set('Trunk') }} {{ trunk_vlans | set('all') }}
Example-2
Unconditional set - in this example “interface_role” will be statically set to “Uplink”, but value for “provider” variable will be taken from template variable “my_var” and set to “L2VC”.
Data:
interface Vlan777
description Management
ip address 192.168.0.1/24
vrf MGMT
!
Template:
<vars>
my_var = "L2VC"
</vars>
<group>
interface {{ interface }}
description {{ description }}
ip address {{ ip }}/{{ mask }}
vrf {{ vrf }}
{{ interface_role | set("Uplink") }}
{{ provider | set("my_var") }}
!{{_end_}}
</group>
Result:
[
{
"description": "Management",
"interface": "Vlan777",
"interface_role": "Uplink",
"ip": "192.168.0.1",
"mask": "24",
"provider": "L2VC",
"vrf": "MGMT"
}
]
replaceall¶
{{ name | replaceall('value1', 'value2', ..., 'valueN') }}
- value (mandatory) - string to replace in match
Run string replace method on match with new and old values derived using below rules.
Case 1 If only one value given new set to ‘’ empty value, if several values specified new set to first value
Example-1.1 With new set to ‘’ empty value
Data:
interface GigabitEthernet3/3
interface GigEthernet5/7
interface GeEthernet1/5
Template:
interface {{ interface | replaceall('Ethernet') }}
Result:
{'interface': 'Gigabit3/3'}
{'interface': 'Gig5/7'}
{'interface': 'Ge1/5'}
Example-1.2 With new set to ‘Ge’
Data:
interface GigabitEthernet3/3
interface GigEth5/7
interface Ethernet1/5
Template:
interface {{ interface | replaceall('Ge', 'GigabitEthernet', 'GigEth', 'Ethernet') }}
Result:
{'interface': 'Ge3/3'}
{'interface': 'Ge5/7'}
{'interface': 'Ge1/5'}
Case 2 If value found in variables that variable used, if variable value is a list, function will iterate over list and for each item run replace where new set either to “” empty or to first value and old equal to each list item
Example-2.1 With new set to ‘GE’ value
Data:
interface GigabitEthernet3/3
interface GigEthernet5/7
interface GeEthernet1/5
Template:
<vars load="python">
intf_replace = ['GigabitEthernet', 'GigEthernet', 'GeEthernet']
</vars>
<group name="ifs">
interface {{ interface | replaceall('GE', 'intf_replace') }}
<group>
Result:
{
"ifs": [
{
"interface": "GE3/3"
},
{
"interface": "GE5/7"
},
{
"interface": "GE1/5"
}
]
}
Example-2.2 With new set to ‘’ empty value
Data:
interface GigabitEthernet3/3
interface GigEthernet5/7
interface GeEthernet1/5
Template:
<vars load="python">
intf_replace = ['GigabitEthernet', 'GigEthernet', 'GeEthernet']
</vars>
<group name="ifs">
interface {{ interface | replaceall('intf_replace') }}
<group>
Result:
{
"ifs": [
{
"interface": "3/3"
},
{
"interface": "5/7"
},
{
"interface": "1/5"
}
]
}
Case 3 If value found in variables that variable used, if variable value is a dictionary, function will iterate over dictionary items and set new to item key and old to item value.
- If item value is a list, function will iterate over list and run replace using each entry as old value
- If item value is a string, function will use that string as old value
Example-3.1 With dictionary values as lists
Data:
interface GigabitEthernet3/3
interface GigEthernet5/7
interface GeEthernet1/5
interface Loopback1/5
interface TenGigabitEth3/3
interface TeGe5/7
interface 10GE1/5
Template:
<vars load="python">
intf_replace = {
'Ge': ['GigabitEthernet', 'GigEthernet', 'GeEthernet'],
'Lo': ['Loopback'],
'Te': ['TenGigabitEth', 'TeGe', '10GE']
}
</vars>
<group name="ifs">
interface {{ interface | replaceall('intf_replace') }}
<group>
Result:
{
"ifs": [
{
"interface": "Ge3/3"
},
{
"interface": "Ge5/7"
},
{
"interface": "Ge1/5"
},
{
"interface": "Lo1/5"
},
{
"interface": "Te3/3"
},
{
"interface": "Te5/7"
}
]
}
resuball¶
{{ name | resuball('value1', 'value2', ..., 'valueN') }}
- value(mandatory) - string to replace in match, can reference template variable name.
Same as replaceall but instead of string replace this function runs python re substitute method, allowing the use of regular expression to match old values.
Example
If new set to “Ge” and old set to “GigabitEthernet”, running string replace against “TenGigabitEthernet” match will produce “Ten” as undesirable result, to overcome that problem regular expressions can be used. For instance, regex “^GigabitEthernet” will only match “GigabitEthernet3/3” as “^” symbol indicates beginning of the string and will not match “GigabitEthernet” in “TenGigabitEthernet”.
Data:
interface GigabitEthernet3/3
interface TenGigabitEthernet3/3
Template:
<vars load="python">
intf_replace = {
'Ge': ['^GigabitEthernet'],
'Te': ['^TenGigabitEthernet']
}
</vars>
<group name="ifs">
interface {{ interface | resuball('intf_replace') }}
<group>
Result:
{
"ifs": [
{
"interface": "Ge3/3"
},
{
"interface": "Ge5/7"
},
{
"interface": "Ge1/5"
},
{
"interface": "Lo1/5"
},
{
"interface": "Te3/3"
},
{
"interface": "Te5/7"
}
]
}
lookup¶
{{ name | lookup('name', 'group', 'template', 'add_field') }}
- name - name of lookup tag and dot-separated path to data within which to perform lookup
- group - dot-separated path to group results to use for lookup
- template - dot-separated path to template results to use for lookup
- add_field - default is False, can be set to string that will indicate name of the new field
Lookup function takes match result value and performs lookup on that value in lookup data structure. Lookup data is a dictionary where keys checked if they are equal to math result.
If lookup was unsuccessful no changes introduces to match result, if it was successful we have two option on what to do with found values: * if add_field is False - match result replaced with found values * if add_field is not False - string passed as add_field value used as a name for additional field that will be added to group match results
Warning
if one group uses results of another group for lookup, these groups must use separate inputs, groups that parse same input data, cannot use each other results for lookup, this is due to the way how TTP combines results on a per-input basis.
Example-1 add_field set to False
In this example, as 65101 will be looked up in the lookup table and replaced with found values
Data:
router bgp 65100
neighbor 10.145.1.9
remote-as 65101
!
neighbor 192.168.101.1
remote-as 65102
Template:
<lookup name="ASNs" load="csv">
ASN,as_name,as_description
65100,Customer_1,Private ASN for CN451275
65101,CPEs,Private ASN for FTTB CPEs
</lookup>
<group name="bgp_config">
router bgp {{ bgp_as }}
<group name="peers">
neighbor {{ peer }}
remote-as {{ remote_as | lookup('ASNs') }}
</group>
</group>
Result:
{
"bgp_config": {
"bgp_as": "65100",
"peers": [
{
"peer": "10.145.1.9",
"remote_as": {
"as_description": "Private ASN for FTTB CPEs",
"as_name": "CPEs"
}
},
{
"peer": "192.168.101.1",
"remote_as": "65102"
}
]
}
}
Example-2 With additional field
Data:
router bgp 65100
neighbor 10.145.1.9
remote-as 65101
!
neighbor 192.168.101.1
remote-as 65102
Template:
<lookup name="ASNs" load="csv">
ASN,as_name,as_description
65100,Customer_1,Private ASN for CN451275
65101,CPEs,Private ASN for FTTB CPEs
</lookup>
<group name="bgp_config">
router bgp {{ bgp_as }}
<group name="peers">
neighbor {{ peer }}
remote-as {{ remote_as | lookup('ASNs', add_field='asn_details') }}
</group>
</group>
Result:
{
"bgp_config": {
"bgp_as": "65100",
"peers": [
{
"asn_details": {
"as_description": "Private ASN for FTTB CPEs",
"as_name": "CPEs"
},
"peer": "10.145.1.9",
"remote_as": "65101"
},
{
"peer": "192.168.101.1",
"remote_as": "65102"
}
]
}
}
Example-3
This example uses group “interfaces_data” results to perform lookup and add additional data in results produced by “arp” group
Template:
<input name="interfaces_data" load="text">
interface FastEthernet2.13
description Customer CPE interface
ip address 10.12.13.1 255.255.255.0
vrf forwarding CPE-VRF
!
interface GigabitEthernet2.13
description Customer CPE interface
ip address 10.12.14.1 255.255.255.0
vrf forwarding CUST1
!
</input>
<group name="interfaces.{{ interface }}" input="interfaces_data">
interface {{ interface }}
description {{ description | ORPHRASE }}
ip address {{ subnet | PHRASE | to_ip | network | to_str }}
vrf forwarding {{ vrf }}
</group>
<input name="arp_data" load="text">
Protocol Address Age (min) Hardware Addr Type Interface
Internet 10.12.13.2 98 0950.5785.5cd1 ARPA FastEthernet2.13
Internet 10.12.14.3 131 0150.7685.14d5 ARPA GigabitEthernet2.13
</input>
<group name="arp" input="arp_data">
Internet {{ ip }} {{ age | DIGIT }} {{ mac }} ARPA {{ interface | lookup(group="interfaces", add_field="subnet_info") }}
</group>
Results:
[
[
{
"interfaces": {
"FastEthernet2.13": {
"description": "Customer CPE interface",
"subnet": "10.12.13.0/24",
"vrf": "CPE-VRF"
},
"GigabitEthernet2.13": {
"description": "Customer CPE interface",
"subnet": "10.12.14.0/24",
"vrf": "CUST1"
}
}
},
{
"arp": [
{
"age": "98",
"interface": "FastEthernet2.13",
"ip": "10.12.13.2",
"mac": "0950.5785.5cd1",
"subnet_info": {
"description": "Customer CPE interface",
"subnet": "10.12.13.0/24",
"vrf": "CPE-VRF"
}
},
{
"age": "131",
"interface": "GigabitEthernet2.13",
"ip": "10.12.14.3",
"mac": "0150.7685.14d5",
"subnet_info": {
"description": "Customer CPE interface",
"subnet": "10.12.14.0/24",
"vrf": "CUST1"
}
}
]
}
]
]
Example-4
In this example, second template uses template “interfaces_data” results to perform lookup by denoting name of the template and path to lookup data in “interfaces_data.interfaces” lookup function template argument.
Template:
<template name="interfaces_data">
<input load="text">
interface FastEthernet2.13
description Customer CPE interface
ip address 10.12.13.1 255.255.255.0
vrf forwarding CPE-VRF
!
interface GigabitEthernet2.13
description Customer CPE interface
ip address 10.12.14.1 255.255.255.0
vrf forwarding CUST1
!
</input>
<group name="interfaces.{{ interface }}">
interface {{ interface }}
description {{ description | ORPHRASE }}
ip address {{ subnet | PHRASE | to_ip | network | to_str }}
vrf forwarding {{ vrf }}
</group>
</template>
<template>
<input load="text">
Protocol Address Age (min) Hardware Addr Type Interface
Internet 10.12.13.2 98 0950.5785.5cd1 ARPA FastEthernet2.13
Internet 10.12.14.3 131 0150.7685.14d5 ARPA GigabitEthernet2.13
</input>
<group name="arp">
Internet {{ ip }} {{ age | DIGIT }} {{ mac }} ARPA {{ interface | lookup(template="interfaces_data.interfaces", add_field="subnet_info") }}
</group>
</template>
Results:
[
[
{
"interfaces": {
"FastEthernet2.13": {
"description": "Customer CPE interface",
"subnet": "10.12.13.0/24",
"vrf": "CPE-VRF"
},
"GigabitEthernet2.13": {
"description": "Customer CPE interface",
"subnet": "10.12.14.0/24",
"vrf": "CUST1"
}
}
}
],
[
{
"arp": [
{
"age": "98",
"interface": "FastEthernet2.13",
"ip": "10.12.13.2",
"mac": "0950.5785.5cd1",
"subnet_info": {
"description": "Customer CPE interface",
"subnet": "10.12.13.0/24",
"vrf": "CPE-VRF"
}
},
{
"age": "131",
"interface": "GigabitEthernet2.13",
"ip": "10.12.14.3",
"mac": "0150.7685.14d5",
"subnet_info": {
"description": "Customer CPE interface",
"subnet": "10.12.14.0/24",
"vrf": "CUST1"
}
}
]
}
]
]
rlookup¶
{{ name | rlookup('name', 'add_field') }}
- name(mandatory) - rlookup table name and dot-separated path to data within which to perform search
- add_field(optional) - default is False, can be set to string that will indicate name of the new field
This function searches rlookup table keys in match value. rlookup table is a dictionary data where keys checked if they are equal to math result.
If lookup was unsuccessful no changes introduces to match result, if it was successful we have two options: * if add_field is False - match Result replaced with found values * if add_field is not False - string passed as add_field used as a name for additional field to be added to group results, value for that new field is a data from lookup table
Example
In this example, bgp neighbors descriptions set to hostnames of peering devices, usually hostnames tend to follow some naming convention to indicate physical location of device or its network role, in below example, naming convention is <state>-<city>-<role><num>
Data:
router bgp 65100
neighbor 10.145.1.9
description vic-mel-core1
!
neighbor 192.168.101.1
description qld-bri-core1
Template:
<lookup name="locations" load="ini">
[cities]
-mel- : 7 Name St, Suburb A, Melbourne, Postal Code
-bri- : 8 Name St, Suburb B, Brisbane, Postal Code
</lookup>
<group name="bgp_config">
router bgp {{ bgp_as }}
<group name="peers">
neighbor {{ peer }}
description {{ remote_as | rlookup('locations.cities', add_field='location') }}
</group>
</group>
Result:
{
"bgp_config": {
"bgp_as": "65100",
"peers": [
{
"description": "vic-mel-core1",
"location": "7 Name St, Suburb A, Melbourne, Postal Code",
"peer": "10.145.1.9"
},
{
"description": "qld-bri-core1",
"location": "8 Name St, Suburb B, Brisbane, Postal Code",
"peer": "192.168.101.1"
}
]
}
}
gpvlookup¶
{{ name | gpvlookup('name', 'add_field', 'record', 'multimatch') }}
- name - name of lookup tag and dot-separated path to data within which to perform lookup
- add_field - default is False, can be set to string that will indicate name of the new field to add with lookup results
- record - default is False, if True will record lookup results in TTP global and parsing object variables for reference by ‘set’ function
- multimatch - default is False, will return first match only as lookup result, if True will iterate over all pasterns and return all found lookup matches
Glob Patterns Values Lookup (gpvookup) function takes match result value and performs lookup on it using lookup data structure. This function can be useful to classify matching results and en-reach parsing output with additional information.
Lookup data is a dictionary of key value pairs, where value is a list of Unix glob patterns to check, if at least one pattern matches, key added to found values list. Found values list is a result produced by this function.
If lookup was unsuccessful no changes introduces to match result, if it was successful we have two option on what to do with found values: * if add_field is False - match result replaced with found values list * if add_field is not False - string passed as add_field value used as a name for additional field that will be added to group match results
If record set to True, gpvlookup function will record found values list in TTP parser and global variables scopes.
Example-1
Basic example of gpvlookup usage. Here matched hostnames got classified by network domain based on glob patterns matching against them.
Template:
<input load="text">
hostname DC1-SW-2
hostname A1-CORP-SW-2
hostname WIFI-CORE-RT-1
hostname DC2-CORP-FW-02
</input>
<lookup name="domains" load="python">
{
"NETWORK_DOMAINS": {
"corporate": ["*CORP*", "WIFI-*"],
"datacentre": ["DC1-*", "DC2-*"]
}
}
</lookup>
<group name="devices">
hostname {{ hostname | gpvlookup("domains.NETWORK_DOMAINS", add_field="Network Domains") }}
</group>
Results:
[
[
{
"devices": [
{
"Network Domains": [
"datacentre"
],
"hostname": "DC1-SW-2"
},
{
"Network Domains": [
"corporate"
],
"hostname": "A1-CORP-SW-2"
},
{
"Network Domains": [
"corporate"
],
"hostname": "WIFI-CORE-RT-1"
},
{
"Network Domains": [
"corporate"
],
"hostname": "DC2-CORP-FW-02"
}
]
}
]
]
Because lookup data is actually a dictionary, first match will be non-deterministic. For instance, in above example hostname DC2-CORP-FW-02 was matched by “corporate” patterns, but not by “datacentre” patterns, even though “datacentre” patterns would produce positive match as well.
Example-2
In this example multimatch used to collect all matches, in addition to that values found by lookup will be recorded in variable “domain” using “record” argument.
Template:
<input load="text">
hostname DC1-WIFI-CORE-RT-1
!
interface Lo0
ip address 5.3.3.3/32
</input>
<input load="text">
hostname WIFI-CORE-RT-1
!
interface Lo0
ip address 6.3.3.3/32
</input>
<lookup name="domains" load="python">
{
"NETWORK_DOMAINS": {
"corporate": ["*WIFI-*"],
"datacentre": ["DC1-*"]
}
}
</lookup>
<group void="">
hostname {{ hostname | gpvlookup("domains.NETWORK_DOMAINS", multimatch=True, record="domain") }}
</group>
<group name="device.{{ interface }}">
interface {{ interface }}
ip address {{ ip }}
{{ domain | set(domain) }}
</group>
Results:
[
[
{
"device": {
"Lo0": {
"domain": [
"corporate",
"datacentre"
],
"ip": "5.3.3.3/32"
}
}
},
{
"device": {
"Lo0": {
"domain": [
"corporate"
],
"ip": "6.3.3.3/32"
}
}
}
]
]
Group function “void” used to deny match results for this particular group to make output cleaner.
geoip_lookup¶
{{ name | geoip_lookup(db_name, add_field) }}
- db_name - Name of the input that contains GeoIP2 database OS absolute path, supporteddatabases are ASN, Country or City
- add_field - default is “geoip_lookup”, can be set to string that will indicate name of new field to use for lookup results
geoip_lookup function use GeoIP2 databases to create Python geoip2 module lookup objects that can be used to enreach results output with information about BGP ASN, Country or City associated with given IP address. db_name reference to lookup tag name with database type separated by dot, such as lookup_tag_name.database_name, reference geoip2 database on how to properly structure lookup tag.
This function need valid IPv4 orIPv6 address as an input to perfrom lookup against.
Prerequisites
Relies on Python geoip2 module, hence it need to be installed on the system.
Example
Template:
<input load="text">
interface Lo0
ip address 123.209.0.1 32
</input>
<lookup name="geoip2_test" database="geoip2">
citY = 'C:/path/to/GeoLite2-City.mmdb'
AsN = 'C:/path/to/GeoLite2-ASN.mmdb'
Country = 'C:/path/to/GeoLite2-Country.mmdb'
</lookup>
<group name="intf_with_city_data">
interface {{ interface }}
ip address {{ ip | geoip_lookup(db_name="geoip2_test.citY", add_field="city_data") }} {{ mask }}
</group>
<group name="intf_with_asn_data">
interface {{ interface }}
ip address {{ ip | geoip_lookup("geoip2_test.AsN", add_field="asn_data") }} {{ mask }}
</group>
<group name="intf_with_country_data">
interface {{ interface }}
ip address {{ ip | geoip_lookup("geoip2_test.Country", "country_data") }} {{ mask }}
</group>
Results:
[
[
{
"intf_with_asn_data": {
"asn_data": {
"ASN": 1221,
"network": "123.209.0.0/16",
"organization": "Telstra Corporation Ltd"
},
"interface": "Lo0",
"ip": "123.209.0.1",
"mask": "32"
},
"intf_with_city_data": {
"city_data": {
"accuracy_radius": 100,
"city": "Olinda",
"continent": "Oceania",
"country": "Australia",
"country_iso_code": "AU",
"latitude": -37.8596,
"longitude": 145.3711,
"network": "123.209.0.0/19",
"postal_code": "3788",
"state": "Victoria",
"state_iso_code": "VIC"
},
"interface": "Lo0",
"ip": "123.209.0.1",
"mask": "32"
},
"intf_with_country_data": {
"country_data": {
"continent": "Oceania",
"continent_code": "OC",
"country": "Australia",
"country_iso_code": "AU",
"network": "123.208.0.0/14"
},
"interface": "Lo0",
"ip": "123.209.0.1",
"mask": "32"
}
}
]
]
startswith_re¶
{{ name | startswith_re('pattern') }}
- pattern(mandatory) - string pattern to check or name of variable from <vars> tag.
Python re search used to evaluate if match value starts with given string pattern, returns True if so and False otherwise
endswith_re¶
{{ name | endswith_re('pattern') }}
- pattern(mandatory) - string pattern to check or name of variable from <vars> tag.
Python re search used to evaluate if match value ends with given string pattern, returns True if so and False otherwise
contains_re¶
{{ name | contains_re('pattern') }}
- pattern(mandatory) - string pattern to check or name of variable from <vars> tag.
Python re search used to evaluate if match value contains given string pattern, returns True if so and False otherwise
contains¶
{{ name | contains('pattern1, pattern2, ... , patternN') }}
- patternN - string pattern to check or name of variable from <vars> tag.
This function evaluates if match value contains at least one of the given patterns, returns True if so and False otherwise.
Example
contains can be used to filter group results based on filtering start REs, for instance, if we have configuration of networking device and we want to extract information only about Vlan interfaces.
Data:
interface Vlan123
description Desks vlan
ip address 192.168.123.1 255.255.255.0
!
interface GigabitEthernet1/1
description to core-1
!
interface Vlan222
description Phones vlan
ip address 192.168.222.1 255.255.255.0
!
interface Loopback0
description Routing ID loopback
Template:
<group name="SVIs">
interface {{ interface | contains('Vlan') }}
description {{ description | ORPHRASE}}
ip address {{ ip }} {{ mask }}
</group>
Result:
{
"SVIs": [
{
"description": "Desks vlan",
"interface": "Vlan123",
"ip": "192.168.123.1",
"mask": "255.255.255.0"
},
{
"description": "Phones vlan",
"interface": "Vlan222",
"ip": "192.168.222.1",
"mask": "255.255.255.0"
}
]
}
If first line in the group contains match variables it is considered start re, if start re condition check result evaluated to False, all the matches that belong to this group will be filtered. In example above line “interface {{ interface | contains(‘Vlan’) }}” is a start re, hence if “interface” variable match will not contain “Vlan”, group results will be discarded.
notstartswith_re¶
{{ name | notstartswith_re('pattern') }}
- pattern(mandatory) - string pattern to check or name of variable from <vars> tag.
Python re search used to evaluate if match value starts with given string pattern, returns False if so and True otherwise
notendswith_re¶
{{ name | notendswith_re('pattern') }}
- pattern(mandatory) - string pattern to check or name of variable from <vars> tag.
Python re search used to evaluate if match value ends with given string pattern, returns False if so and True otherwise
exclude_re¶
{{ name | exclude_re('pattern') }}
- pattern(mandatory) - string pattern to check or name of variable from <vars> tag.
Python re search used to evaluate if match value contains given string pattern, returns False if so and True otherwise
exclude¶
{{ name | exclude('pattern') }}
- pattern(mandatory) - string pattern to check or name of variable from <vars> tag.
This function evaluates if match value contains given string pattern, returns False if so and True otherwise.
equal¶
{{ name | equal('value') }}
- value(mandatory) - string pattern to check or name of variable from <vars> tag.
This function evaluates if match is equal to given value, returns True if so and False otherwise
notequal¶
{{ name | notequal('value') }}
- value(mandatory) - string pattern to check or name of variable from <vars> tag.
This function evaluates if match is equal to given value, returns False if so and True otherwise
isdigit¶
{{ name | isdigit }}
This function checks if match is a digit, returns True if so and False otherwise
notdigit¶
{{ name | notdigit }}
This function checks if match is digit, returns False if so and True otherwise
greaterthan¶
{{ name | greaterthan('value') }}
- value(mandatory) - integer value to compare with
This function checks if match and supplied value are digits and performs comparison operation, if match is bigger than given value returns True and False otherwise
lessthan¶
{{ name | lessthan('value') }}
- value(mandatory) - integer value to compare with
This function checks if match and supplied value are digits and performs comparison, if match is smaller than provided value returns True and False otherwise
item¶
{{ name | item(item_index) }}
- item_index(mandatory) - integer, index of item to return
Return item value at given index of iterable. If match result (iterable) is string, item returns letter at given index, if match been transformed to list by the moment item function runs, returns list item at given index. item_index can be positive or negative digit, same rules as for retrieving list items applies e.g. if item_index is -1, last item will be returned.
In addition, ttp preforms index out of range checks, returning last or first item if item_index exceeds length of match result.
macro¶
{{ name | macro(macro_name) }}
- macro_name(mandatory) - name of macro function to pass match result through
Macro brings Python language capabilities to match results processing and validation during ttp module execution, as it allows to run custom functions against match results. Macro functions referenced by their name in match variable definitions or as a group macro attribute.
Warning
macro uses python exec
function to parse code payload without imposing any restrictions, hence it is dangerous to run templates from untrusted sources as they can have macro defined in them that can be used to execute any arbitrary code on the system.
Macro function must accept only one attribute to hold match results, for match variable data supplied to macro function is a match result string.
For match variables, depending on data returned by macro function, ttp will behave differently according to these rules:
- If macro returns True or False - original data unchanged, macro handled as condition functions, invalidating result on False and keeps processing result on True
- If macro returns None - data processing continues, no additional logic associated
- If macro returns single item - that item replaces original data supplied to macro and processed further
- If macro return tuple of two elements - fist element must be string - match result, second - dictionary of additional fields to add to results
Note
Macro function contained within <macro>
tag, each function loaded and saved into the dictionary of function name and function object, as a result cross referencing macro functions is not supported.
Example
In this example macro functions referenced in match variables.
Template:
<input load="text">
interface Vlan123
description Desks vlan
ip address 192.168.123.1 255.255.255.0
!
interface GigabitEthernet1/1
description to core-1
!
interface Vlan222
description Phones vlan
ip address 192.168.222.1 255.255.255.0
!
interface Loopback0
description Routing ID loopback
!
</input>
<macro>
def check_if_svi(data):
if "Vlan" in data:
return data, {"is_svi": True}
else:
return data, {"is_svi": False}
def check_if_loop(data):
if "Loopback" in data:
return data, {"is_loop": True}
else:
return data, {"is_loop": False}
</macro>
<macro>
def description_mod(data):
# To revert words order in descripotion
words_list = data.split(" ")
words_list_reversed = list(reversed(words_list))
words_reversed = " ".join(words_list_reversed)
return words_reversed
</macro>
<group name="interfaces_macro">
interface {{ interface | macro("check_if_svi") | macro("check_if_loop") }}
description {{ description | ORPHRASE | macro("description_mod")}}
ip address {{ ip }} {{ mask }}
</group>
Result:
[
{
"interfaces_macro": [
{
"description": "vlan Desks",
"interface": "Vlan123",
"ip": "192.168.123.1",
"is_loop": false,
"is_svi": true,
"mask": "255.255.255.0"
},
{
"description": "core-1 to",
"interface": "GigabitEthernet1/1",
"is_loop": false,
"is_svi": false
},
{
"description": "vlan Phones",
"interface": "Vlan222",
"ip": "192.168.222.1",
"is_loop": false,
"is_svi": true,
"mask": "255.255.255.0"
},
{
"description": "loopback ID Routing",
"interface": "Loopback0",
"is_loop": true,
"is_svi": false
}
]
}
]
to_list¶
{{ name | to_list }}
to_list transform match result in python list object in such a way that if match result is a string, empty lit will be created and result will be appended to it, if match result not a string by the time to_list function runs, this function does nothing.
Example
Template:
<input load="text" name="test1-18">
interface GigabitEthernet1/1
description to core-1
ip address 192.168.123.1 255.255.255.0
!
</input>
<group name="interfaces_functions_test1_18"
input="test1-18"
output="test1-18"
>
interface {{ interface }}
description {{ description | ORPHRASE | split(" ") | to_list }}
ip address {{ ip | to_list }} {{ mask }}
</group>
Result:
[{
"interfaces_functions_test1_18": {
"description": [
"to",
"core-1"
],
"interface": "GigabitEthernet1/1",
"ip": [
"192.168.123.1"
],
"mask": "255.255.255.0"
}
}]
to_str¶
{{ name | to_str }}
This function transforms match result to string object running python str(match_result)
built-in function, that is useful for such a cases when match result been transformed to some other object during processing and it needs to be converted back to string.
to_int¶
{{ name | to_int }}
This function will try to transforms match result into integer object running python int(match_result)
built-in function, if it fails to do so, execution will continue, results will not e invalidated. to_int is useful if you need to convert string representation of integer in actual integer object to run mathematical operation with it.
to_ip¶
{{ name | to_ip }}
or {{ name | to_ip("ipv4") }}
- to_ip(version) - uses python ipaddress module to transform match result in one of ipaddress supported objects, by default will use ipaddress module built-in logic to determine version of IP address, optionally version can be provided using ipv4 or ipv6 arguments to create IPv4Address or IPv6Address ipaddress module objects. In addition ttp does the check to detect if slash “/” present - e.g. 137.168.1.3/27 - in match result or space ” ” present in match result - e.g. 137.168.1.3 255.255.255.224, if so it will create IPInterface, IPv4Interface or IPv6Interface object depending on provided arguments.
After match result transformed into ipaddress’ IPaddress or IPInterface object, built-in functions and attributes of these objects can be called using match variable functions chains.
Note
reference ipaddress module documentation for complete list of functions and attributes
Example
It is often that devices use “ip address 137.168.1.3 255.255.255.224” syntaxes to configure interface’s IP addresses, let’s assume we need to convert it to “137.168.1.3/27” representation and vice versa.
Template:
<input load="text">
interface Loopback0
ip address 1.0.0.3 255.255.255.0
!
interface Vlan777
ip address 192.168.0.1/24
!
</input>
<group name="interfaces">
interface {{ interface }}
ip address {{ ip | PHRASE | to_ip | with_prefixlen }}
ip address {{ ip | to_ip | with_netmask }}
</group>
Result:
[
{
"interfaces": [
{
"interface": "Loopback0",
"ip": "1.0.0.3/24"
},
{
"interface": "Vlan777",
"ip": "192.168.0.1/255.255.255.0"
}
]
}
]
with_prefixlen and with_netmask are python ipaddress module IPv4Interface object’s built-in functions.
to_net¶
{{ name | to_net }}
- This function leverages python built-in ipaddress module to transform match result into IPNetwork object provided that match is a valid ipv4 or ipv6 network strings e.g. 192.168.0.0/24
- or fe80:ab23::/64.
Example
Let’s assume we need to get results for private routes only from below data, to_net can be used to transform match result into network object together with IPNetwork built-in function is_private to filter results.
Template:
<input load="text">
RP/0/0/CPU0:XR4#show route
i L2 10.0.0.2/32 [115/20] via 10.0.0.2, 00:41:40, tunnel-te100
i L2 172.16.0.3/32 [115/10] via 10.1.34.3, 00:45:11, GigabitEthernet0/0/0/0.34
i L2 1.1.23.0/24 [115/20] via 10.1.34.3, 00:45:11, GigabitEthernet0/0/0/0.34
</input>
<group name="routes">
{{ code }} {{ subcode }} {{ net | to_net | is_private | to_str }} [{{ ad }}/{{ metric }}] via {{ nh_ip }}, {{ age }}, {{ nh_interface }}
</group>
Result:
[
{
"routes": [
{
"ad": "115",
"age": "00:41:40",
"code": "i",
"metric": "20",
"net": "10.0.0.2/32",
"nh_interface": "tunnel-te100",
"nh_ip": "10.0.0.2",
"subcode": "L2"
},
{
"ad": "115",
"age": "00:45:11",
"code": "i",
"metric": "10",
"net": "172.16.0.3/32",
"nh_interface": "GigabitEthernet0/0/0/0.34",
"nh_ip": "10.1.34.3",
"subcode": "L2"
}
]
}
]
is_private check invalidated public 1.1.23.0/24 subnet and only private networks were included in results.
to_cidr¶
{{ name | to_cidr }}
Function to convert subnet mask in prefix length representation, for instance if match result is “255.255.255.0”, to_cidr function will return “24”
ip_info¶
{{ name | ip_info }}
Python ipaddress module helps to convert plain text string into IP addresses objects, as part of that process ipaddress module calculates a lot of additional information, ip_info function retrieves that information from that object and returns it in dictionary format.
Example
Below loopback0 IP address will be converted to IPv4Address object and ip_info will return information about that IP only, for other interfaces ttp will be able to create IPInterface objects, that apart from IP details contains information about network.
Template:
<input load="text">
interface Loopback0
ip address 1.0.0.3 255.255.255.0
!
interface Vlan777
ip address 192.168.0.1/24
!
interface Vlan777
ip address fe80::fd37/124
!
</input>
<group name="interfaces">
interface {{ interface }}
ip address {{ ip | to_ip | ip_info }} {{ mask }}
ip address {{ ip | to_ip | ip_info }}
</group>
Result:
[
{
"interfaces": [
{
"interface": "Loopback0",
"ip": {
"compressed": "1.0.0.3",
"exploded": "1.0.0.3",
"ip": "1.0.0.3",
"is_link_local": false,
"is_loopback": false,
"is_multicast": false,
"is_private": false,
"is_reserved": false,
"is_unspecified": false,
"max_prefixlen": 32,
"version": 4
},
"mask": "255.255.255.0"
},
{
"interface": "Vlan777",
"ip": {
"broadcast_address": "192.168.0.255",
"compressed": "192.168.0.1/24",
"exploded": "192.168.0.1/24",
"hostmask": "0.0.0.255",
"hosts": 254,
"ip": "192.168.0.1",
"is_link_local": false,
"is_loopback": false,
"is_multicast": false,
"is_private": true,
"is_reserved": false,
"is_unspecified": false,
"max_prefixlen": 32,
"netmask": "255.255.255.0",
"network": "192.168.0.0/24",
"network_address": "192.168.0.0",
"num_addresses": 256,
"prefixlen": 24,
"version": 4,
"with_hostmask": "192.168.0.1/0.0.0.255",
"with_netmask": "192.168.0.1/255.255.255.0",
"with_prefixlen": "192.168.0.1/24"
}
},
{
"interface": "Vlan777",
"ip": {
"broadcast_address": "fe80::fd3f",
"compressed": "fe80::fd37/124",
"exploded": "fe80:0000:0000:0000:0000:0000:0000:fd37/124",
"hostmask": "::f",
"hosts": 14,
"ip": "fe80::fd37",
"is_link_local": true,
"is_loopback": false,
"is_multicast": false,
"is_private": true,
"is_reserved": false,
"is_unspecified": false,
"max_prefixlen": 128,
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0",
"network": "fe80::fd30/124",
"network_address": "fe80::fd30",
"num_addresses": 16,
"prefixlen": 124,
"version": 6,
"with_hostmask": "fe80::fd37/::f",
"with_netmask": "fe80::fd37/ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0",
"with_prefixlen": "fe80::fd37/124"
}
}
]
}
]
is_ip¶
{{ name | is_ip }}
is_ip function tries to convert provided match result in Python ipaddress module IPAddress or IPInterface object, if that happens without any exceptions (errors), is_ip returns True and False otherwise.
Example
Template:
<input load="text">
interface Loopback0
ip address 192.168.0.113/24
!
interface Loopback1
ip address 192.168.1.341/24
!
</input>
<group name="interfaces">
interface {{ interface }}
ip address {{ ip | is_ip }}
</group>
Result:
[
{
"interfaces": [
{
"interface": "Loopback0",
"ip": "192.168.0.113/24"
},
{
"interface": "Loopback1"
}
]
}
]
192.168.1.341/24 match result was invalidated as it is not a valid IP address.
cidr_match¶
{{ name | cidr_match(prefix) }}
prefix
- IPv4 or IPv6 prefix string, for instance ‘10.0.0.0/16’ or name of <vars> tag variable.
This function allows to convert provided prefix in ipaddress IPNetwork object and convert match_result into IPInterface object, after that, cidr_match will run overlaps check to see if provided prefix and match result ip address overlapping, returning Trueif so and False otherwise, allowing to filter match results based on that.
Example-1
In example below, IP of Loopback1 interface is not overlapping with 192.168.0.0/16 range, hence it will be invalidated.
Template:
<input load="text">
interface Loopback0
ip address 192.168.0.113/24
!
interface Loopback1
ip address 10.0.1.251/24
!
</input>
<group name="interfaces">
interface {{ interface }}
ip address {{ ip | cidr_match("192.168.0.0/16") }}
</group>
Result:
[{
"interfaces": [
{
"interface": "Loopback0",
"ip": "192.168.0.113/24"
},
{
"interface": "Loopback1"
}
]
}]
Example-1
In example below, cidr_match references <vars> tag variable - subnet
Template:
<input load="text">
interface Lo0
ip address 124.171.238.50 32
!
interface Lo1
ip address 1.1.1.1 32
</input>
<vars>
subnet="1.1.1.0/24"
</vars>
<group contains="ip">
interface {{ interface }}
ip address {{ ip | cidr_match(subnet) }} {{ mask }}
</group>
Result:
[
[
{
"interface": "Lo1",
"ip": "1.1.1.1",
"mask": "32"
}
]
]
dns¶
{{ name | dns(record='A', timeout=1, servers=[], add_field=False) }}
This function performs forward DNS lookup of match results and returns sorted list of IP addresses returned by DNS.
Prerequisites: dnspython needs to be installed
Options:
record
- by default perform ‘A’ lookup, any dnspython supported record can be given, e.g. ‘AAAA’ for IPv6 lookuptimeout
- default is 1 second, amount of time to wait for response, overall lifetime of operation will be set to number of servers multiplied by timeoutservers
- comma separated string of DNS servers to use for lookup, by default uses DNS servers configured on machine running the codeadd_field
- boolean or string, if string, its value will be used as a key for DNS lookup results, if False - DNS lookup results will replace match results
If DNS will fail for whatever reason, match results will be returned without any modifications.
Example
Template:
<input load="text">
interface GigabitEthernet3/11
description wikipedia.org
!
</input>
<group name="interfaces">
interface {{ interface }}
description {{ description | dns }}
</group>
<group name="interfaces_dnsv6">
interface {{ interface }}
description {{ description | dns(record='AAAA') }}
</group>
<group name="interfaces_dnsv4_google_dns">
interface {{ interface }}
description {{ description | dns(record='A', servers='8.8.8.8') }}
</group>
<group name="interfaces_dnsv6_add_field">
interface {{ interface }}
description {{ description | dns(record='AAAA', add_field='IPs') }}
</group>
Result:
[
{
"interfaces": {
"description": [
"103.102.166.224"
],
"interface": "GigabitEthernet3/11"
},
"interfaces_dnsv4_google_dns": {
"description": [
"103.102.166.224"
],
"interface": "GigabitEthernet3/11"
},
"interfaces_dnsv6": {
"description": [
"2001:df2:e500:ed1a::1"
],
"interface": "GigabitEthernet3/11"
},
"interfaces_dnsv6_add_field": {
"IPs": [
"2001:df2:e500:ed1a::1"
],
"description": "wikipedia.org",
"interface": "GigabitEthernet3/11"
}
}
]
rdns¶
{{ name | dns(timeout=1, servers=[], add_field=False) }}
This function performs reverse DNS lookup of match results and returns FQDN obtained from DNS.
Prerequisites: dnspython needs to be installed
Arguments:
timeout
- default is 1 second, amount of time to wait for response, overall lifetime of operation will be set to number of servers multiplied by timeoutservers
- comma separated string of DNS servers to use for lookup, by default uses DNS servers configured on machine running the codeadd_field
- boolean or string, if string, its value will be used as a key for DNS lookup results, if False - DNS lookup results will replace match results
If DNS will fail for whatever reason, match results will be returned without any modifications.
Example
Template:
<input load="text">
interface GigabitEthernet3/11
ip address 8.8.8.8 255.255.255.255
!
</input>
<group name="interfaces_rdns">
interface {{ interface }}
ip address {{ ip | rdns }} {{ mask }}
</group>
<group name="interfaces_rdns_google_server">
interface {{ interface }}
ip address {{ ip | rdns(servers='8.8.8.8') }} {{ mask }}
</group>
<group name="interfaces_rdns_add_field">
interface {{ interface }}
ip address {{ ip | rdns(add_field='FQDN') }} {{ mask }}
</group>
Result:
[
{
"interfaces_rdns_add_field": {
"FQDN": "dns.google",
"interface": "GigabitEthernet3/11",
"ip": "8.8.8.8",
"mask": "255.255.255.255"
},
"interfaces_rdnsv4": {
"interface": "GigabitEthernet3/11",
"ip": "dns.google",
"mask": "255.255.255.255"
},
"interfaces_rdnsv4_google_server": {
"interface": "GigabitEthernet3/11",
"ip": "dns.google",
"mask": "255.255.255.255"
}
}
]
sformat¶
{{ name | sformat("value") }}
- value - string to format with match result or name of variable for from <vars> tag.
sformat allows to embed match result within arbitrary string using syntaxis supported by python built-in format function.
Example
Template:
<input load="text">
interface Vlan778
ip address 2002:fd37::91/124
!
</input>
<group name="interfaces">
interface {{ interface }}
ip address {{ ip | sformat("ASN 65100 IP - {}") }}
</group>
Results:
[
{
"interfaces": {
"interface": "Vlan778",
"ip": "ASN 65100 IP - 2002:fd37::91/124"
}
}
]
uptimeparse¶
{{ name | uptimeparse }}
or {{ name | uptimeparse(format="seconds|dict") }}
This function can be used to parse text strings of below format to extract uptime information:
2 years, 5 months, 27 weeks, 3 days, 10 hours, 46 minutes
27 weeks, 3 days, 10 hours, 46 minutes
10 hours, 46 minutes
1 minutes
Arguments:
format
- default is seconds, optional argument to specify format of returned results, if seconds - integer, number of seconds will be returned, if dict - will return a dictionary of extracted time
Example
Template:
<input load="text">
device-hostame uptime is 27 weeks, 3 days, 10 hours, 46 minutes, 10 seconds
</input>
<group name="uptime-1-seconds">
device-hostame uptime is {{ uptime | PHRASE | uptimeparse }}
</group>
<group name="uptime-2-dictionary">
device-hostame uptime is {{ uptime | PHRASE | uptimeparse(format="dict") }}
</group>
Results:
[
{
"uptime-1-seconds": {
"uptime": 16627570
},
"uptime-2-dictionary": {
"uptime": {
"days": "3",
"hours": "10",
"mins": "46",
"secs": "10",
"weeks": "27"
}
}
}
]
mac_eui¶
{{ name | mac_eui }}
This function normalizes mac address representation format by deleting -:.
characters from mac address string and converting it into aa:bb:cc:dd:ee:ff. It also handles the case when mac address trailing zeros stripped by device in show commands output, by staffing zeros to make mac address 12 symbols long, e.g. aabb.ccdd.ee will be converted to aa:bb:cc:dd:ee:00
count¶
{{ name | count(var="per_input_counter", globvar="global_counter") }}
- var - string, name of per input variable to store count results
- globvar - string, name of global variable to store count results across several input datums
This function introduces counting capabilities, allowing to increase counter variable on every successful match. There are two types of count variables supported - per input and global, as the names imply, per input variable has input significance, while global variable can help to count matches across several inputs.
Example
Let’s say we need to count a number of interfaces in up state for each device and across all devices.
Template:
<input name="device-1" load="text">
device-1#show ip int brief
Interface IP-Address OK? Method Status Protocol
GigabitEthernet0/2 unassigned YES unset up up
GigabitEthernet0/3 unassigned YES unset up up
GigabitEthernet0/4 unassigned YES unset down down
</input>
<input name="device-2" load="text">
device-2#show ip int brief
Interface IP-Address OK? Method Status Protocol
Vlan20 172.29.50.3 YES NVRAM down down
Vlan41 172.29.52.34 YES NVRAM up up
GigabitEthernet0/1 unassigned YES unset down down
</input>
<vars name="counters">
interfaces_up = 0
</vars>
<group name="interfaces*">
{{ interface }} {{ ip }} YES {{ ignore }} {{ status | equal("up") | count(var="interfaces_up", globvar="overall_interfaces_up") }} {{ protocol }}
</group>
<output macro="add_glob_counters"/>
<macro>
def add_glob_counters(data):
data.append({ "overall_interfaces_up": _ttp_["global_vars"]["overall_interfaces_up"] })
</macro>
Results:
[
[
{
"counters": {
"interfaces_up": 2
},
"interfaces": [
{
"interface": "GigabitEthernet0/2",
"ip": "unassigned",
"protocol": "up",
"status": "up"
},
{
"interface": "GigabitEthernet0/3",
"ip": "unassigned",
"protocol": "up",
"status": "up"
}
]
},
{
"counters": {
"interfaces_up": 1
},
"interfaces": [
{
"interface": "Vlan41",
"ip": "172.29.52.34",
"protocol": "up",
"status": "up"
}
]
},
{
"overall_interfaces_up": 3
}
]
]
void¶
{{ name | void }}
The purpose of this function is to return False invalidating match results for this variable.
to_float¶
{{ name | to_float }}
This function tries to convert integer expressed as int (e.g. 2) or as a string (e,f, “45”) to python integer of float type, e.g. 2 will be converted to 2.0
to_unicode¶
{{ name | to_unicode }}
If python2 used to run TTP script, this function will try to convert match variable value to unicode string, e.g. string “abc” will become u”abc”